package com.bytebuddytest.fieldandmethod;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.LoadedTypeInitializer;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import org.junit.Assert;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import static net.bytebuddy.matcher.ElementMatchers.named;

/**
 * @author yutianhong
 * @version 1.0
 * @since 2023/09/26 10:58
 */
public class FixedValuesTest {

    public static class Bar {
        public String bar() {
            return null;
        }

        public List<Integer> qux() {
            return null;
        }
    }

    @Test
    public void testConstantPool() throws IOException {
        DynamicType.Unloaded<Bar> dynamicType = new ByteBuddy()
                .subclass(Bar.class)
                .method(named("bar"))
                .intercept(FixedValue.value("i love bytebuddy"))
                .make();

        String folderPath = getClass().getClassLoader().getResource("").getPath() + "testConstantPool";
        System.out.println("folderPath = " + folderPath);
        dynamicType.saveIn(new File(folderPath));
    }

    @Test
    public void testReference() throws Exception {
        String fixedValue = "i love bytebuddy";
        DynamicType.Unloaded<Bar> dynamicType = new ByteBuddy()
                .subclass(Bar.class)
                .method(named("bar"))
                // public static volatile xxx
                .intercept(FixedValue.reference(fixedValue, "BAR_STRING"))
                .make();

        String folderPath = getClass().getClassLoader().getResource("").getPath() + "testReference";
        System.out.println("folderPath = " + folderPath);
        dynamicType.saveIn(new File(folderPath));

        String bar = dynamicType.load(getClass().getClassLoader())
                .getLoaded()
                .newInstance()
                .bar();
        Assert.assertSame(bar, fixedValue);
    }


    @Test
    public void testStaticField() throws Exception {
        DynamicType.Unloaded<Bar> dynamicType = new ByteBuddy()
                .subclass(Bar.class)
                .method(named("qux"))
                .intercept(FixedValue.value(Arrays.asList(1, 2, 3)))
                .make();

        String folderPath = getClass().getClassLoader().getResource("").getPath() + "testStaticField";
        System.out.println("folderPath = " + folderPath);
        dynamicType.saveIn(new File(folderPath));

        DynamicType.Loaded<Bar> loadType = dynamicType.load(getClass().getClassLoader());
        Class<? extends Bar> dynamicClass = loadType.getLoaded();

        // although already initialized, but we can try again
        LoadedTypeInitializer typeInitializer = loadType.getLoadedTypeInitializers()
                .get(dynamicType.getTypeDescription());
        if (typeInitializer.isAlive()) {
            typeInitializer.onLoad(dynamicClass);
        }

        dynamicClass.newInstance().qux().set(0, 8);
        Integer get = dynamicClass.newInstance().qux().get(0);
        Assert.assertEquals(get, Integer.valueOf(8));
    }

    @Test
    public void testIllegalArgument() throws IOException {
        DynamicType.Builder.MethodDefinition.ReceiverTypeDefinition<Bar> typeDefinition = new ByteBuddy()
                .subclass(Bar.class)
                .method(named("bar"))
                .intercept(FixedValue.value(0));
        // return value type is int, but return type is String
        try {
            typeDefinition.make();
            Assert.fail();
        } catch (IllegalArgumentException ignore) {
            return;
        }
        Assert.fail();
    }

    @Test
    public void testAssigner() {
        // we can define custom assigner if we like, or just use default assigner
        // pay attention to "heap pollution"
        DynamicType.Unloaded<Bar> dynamicType = new ByteBuddy()
                .subclass(Bar.class)
                .method(named("bar"))
                .intercept(
                        FixedValue.value("i love bytebuddy")
                                .withAssigner(Assigner.DEFAULT, Assigner.Typing.STATIC)
                )
                .make();
    }

}
